import argparse
import asyncio
import inspect
import json
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Optional, Sequence, Any

import yaml

from .loop_until_terminated import loop_until_terminated
from ..jsonpat import from_json
from ..logger import TreeLogger, AbstractLogger
from ..logger.writers import FileLogWriter, StdLogWriter
from .run_server import ServerStartOptions, ConfigType, run_default_server
from .json_handler import ContextType


def create_default_logger(root_dir: Path, *, root_name="root", file_keep_days=5, std_log_level="debug"):
    log_writers = [StdLogWriter(level=std_log_level)]
    if file_keep_days != 0:
        log_writers.append(FileLogWriter(root=root_dir, keep_days=file_keep_days))

    return TreeLogger([root_name], log_writers)


class ServerQuickStartOptions(ServerStartOptions[ContextType, ConfigType], ABC):

    def __init__(self, root_dir="./log", *, root_name="root", file_keep_days=5, std_log_level="debug"):
        self._logger = create_default_logger(
            Path(root_dir),
            root_name=root_name,
            file_keep_days=file_keep_days,
            std_log_level=std_log_level
        )

    @property
    def logger(self) -> AbstractLogger:
        return self._logger

    async def on_create_context(self) -> ContextType:
        cmd_args = get_quick_starter_options_from_arg()
        if hasattr(cmd_args, "config"):
            config_file_path = Path(cmd_args.config)
        else:
            config_file_path = Path("./config.json")

        with open(config_file_path, "r") as file:
            extension = config_file_path.suffix.lower()
            if extension == ".json":
                config_json = json.loads(file.read())
            elif extension == ".yaml":
                config_json = yaml.safe_load(file)
            else:
                raise RuntimeError("unknown config file type")

        create_context_specs = inspect.getfullargspec(self.on_create_context_from_config)
        assert 'config' in create_context_specs.kwonlyargs
        config_type = create_context_specs.annotations.get('config', Any)
        config_obj = from_json(config_json, config_type)

        if hasattr(cmd_args, "port"):
            config_obj.port = cmd_args.port

        return await self.on_create_context_from_config(config=config_obj, current_dir=config_file_path.parent, logger=self.logger)

    @abstractmethod
    async def on_create_context_from_config(self, *, config: ConfigType, current_dir: Path, logger: AbstractLogger):
        pass


def get_quick_starter_options_from_arg(args: Optional[Sequence[str]] = None) -> Any:
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--config", type=str, default="config.yaml")
    parser.add_argument("-p", "--port", type=int)
    return parser.parse_args(args)


async def run_quick_starter(
        options: ServerQuickStartOptions[ContextType, ConfigType],
):
    stopper = await run_default_server(options, get_quick_starter_options_from_arg())
    await loop_until_terminated(lambda: asyncio.sleep(0.5))
    await stopper.stop()
